package com.meida.module.arc.provider.log.aop;

import com.beust.jcommander.internal.Maps;
import com.meida.common.base.utils.FlymeUtils;
import com.meida.common.security.OpenHelper;
import com.meida.common.security.OpenUser;
import com.meida.common.thread.IThreadPoolExecutorService;
import com.meida.common.utils.JsonUtils;
import com.meida.common.utils.WebUtils;
import com.meida.module.arc.client.entity.ArcLog;
import com.meida.module.arc.provider.log.annotation.LogAspect;
import com.meida.module.arc.provider.log.convert.LogConvert;
import com.meida.module.arc.provider.service.ArcLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;

/**
 * <b>功能名：LogApo</b><br>
 * <b>说明：</b><br>
 * <b>著作权：</b> Copyright (C) 2021 HUIFANEDU  CORPORATION<br>
 * <b>修改履历：
 *
 * @author 2022-01-23 jiabing
 */
@Component
@Aspect
@Slf4j
public class LogAop {

    @Autowired
    private ArcLogService arcLogService;
    @Autowired
    private IThreadPoolExecutorService threadPoolExecutorService;

    @Pointcut("@annotation(com.meida.module.arc.provider.log.annotation.LogAspect)")
    public void cut(){}

    @After("cut()")
    public void after(JoinPoint jp) throws Throwable{
        try{
            Object[] args = jp.getArgs();
            MethodSignature signature = (MethodSignature)jp.getSignature();
            Method method = signature.getMethod();
            String[] parameterNames = signature.getParameterNames();
            Map<String,Object> paramNameValueMap = Maps.newHashMap();
            for (int i = 0; i < parameterNames.length; i++) {
                paramNameValueMap.put(parameterNames[i],args[i]);
            }

            LogAspect aspectLog = method.getAnnotation(LogAspect.class);
            ArcLog log = new ArcLog();
            OpenUser user = OpenHelper.getUser();
            log.setLogType(aspectLog.logType().getCode());
            log.setOptType(aspectLog.optType().getName());
            log.setOptContent(aspectLog.optContent());
            log.setOptTime(new Date());
            log.setOptObj((aspectLog.optObj()));
            log.setUserName(user.getUsername());
            log.setNickName(user.getNickName());
            if (args != null && args.length > 0) {
                if (args.length == 1) {
                    log.setOptParam(JsonUtils.beanToJson(args[0]));
                } else {
                    Object obj= paramNameValueMap.get("params");
                    if(FlymeUtils.isNotEmpty(obj)) {
                        log.setOptParam(JsonUtils.beanToJson(obj));
                    }
                }
            }
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();

            log.setLoginIp(WebUtils.getRemoteAddress(request));

            Class<? extends LogConvert> convertClazz = aspectLog.convert();

            if (FlymeUtils.isNotEmpty(convertClazz) && LogConvert.class.isAssignableFrom(convertClazz) && !"LogConvert".equals(convertClazz.getSimpleName())) {
                LogConvert convert = convertClazz.newInstance();
                //异步调用原文导出
                threadPoolExecutorService.singleExecute(() -> dealLog(args, convert, log));
            } else {
                //异步调用原文导出
                threadPoolExecutorService.singleExecute(() -> dealLog(args, null, log));
            }



        }catch (Exception e){
            log.error("执行日志切面异常",e);
        }
    }

    private void dealLog(Object[] args,LogConvert convert,ArcLog log){

        boolean logFlag = true;
        if(FlymeUtils.isNotEmpty(convert)){
            logFlag = convert.convert(args,log);
        }
        //异步执行日志插入
        if(logFlag){
            arcLogService.saveLog(log);
        }
    }



    private String getExpressionValue(String expression, Object o){
        String[] expressionItems = expression.split("\\.");
        for(String item : expressionItems){
            if(String.class.isAssignableFrom(o.getClass())){
                return (String)o;
            }else if(Integer.class.isAssignableFrom(o.getClass())) {
                return ((Integer) o).toString();
            }else if(Long.class.isAssignableFrom(o.getClass())){
                return ((Long) o).toString();
            }else if(Double.class.isAssignableFrom(o.getClass())){
                return ((Double) o).toString();
            }else if(BigDecimal.class.isAssignableFrom(o.getClass())){
                return ((BigDecimal) o).toPlainString();
            }else if(Map.class.isAssignableFrom(o.getClass())){
                Object v = ((Map)o).get(item);
                if(v == null){
                    return null;
                }
                return getExpressionValue(getRemainExpression(expression,item),v);
            }else{
                try{
                    Object v = MethodUtils.invokeMethod(o,"get"+item.substring(0,1).toUpperCase() + item.substring(1));
                    if(v == null){
                        return null;
                    }
                    return getExpressionValue(getRemainExpression(expression,item),v);
                }catch (Exception e){
                    return null;
                }
            }
        }
        return null;
    }

    private String getRemainExpression(String expression, String expressionItem) {
        if (expression.equals(expressionItem)) {
            return expressionItem;
        } else {
            return expression.substring(expressionItem.length() + 1);
        }
    }
}
